From a5cc2a5d2fc7074f50fbaa772232b6e0fea7ce89 Mon Sep 17 00:00:00 2001
From: Dave Beckett <dave@dajobe.org>
Date: Sun, 25 Jan 2015 15:44:27 -0800
Subject: [PATCH] Make form use/free api saner and prevent double free. Fixes
 Issue #28

---
 src/common.c             | 31 +++++++++++++++++++++----------
 src/flickcurl_internal.h |  2 +-
 src/oauth.c              | 34 ++++++++++++++++++----------------
 3 files changed, 40 insertions(+), 27 deletions(-)

diff --git a/src/common.c b/src/common.c
index 1fcc67d..348c78f 100644
--- a/src/common.c
+++ b/src/common.c
@@ -1516,14 +1516,18 @@ flickcurl_invoke_get_content(flickcurl *fc, size_t* size_p)
 }
 
 
+/*
+ * INTERNAL - free a form.
+ */
 void
-flickcurl_free_form(char **form, int count)
+flickcurl_free_form(char **form)
 {
   if(!form)
     return;
 
   /* free content which is the first key */
-  free(form[0]);
+  if(form[0])
+    free(form[0]);
 
   free(form);
 }
@@ -1537,10 +1541,16 @@ flickcurl_free_form(char **form, int count)
 * INTERNAL - decoded content from current request as HTTP FORM and return fields
 *
 * NOTE: The result may be an empty array with just two NULL
-* terminating pointers if there are no fields.
+* terminating pointers if there are no fields or no content.
+*
+* If @count_p is not NULL, *@count_p is set to the number of pairs of
+* fields.
+*
+* Index 0 is used to store the raw content.
+*
+* Return value: NULL on failure or an array of [char* field name,
+* char* field value] starting at index 1, terminated by a NULL pair.
 *
-* Return value: array of [char* field name, char* field value] with
-* NULL pair terminating or NULL on failure
 */
 char**
 flickcurl_invoke_get_form_content(flickcurl *fc, int* count_p)
@@ -1562,21 +1572,24 @@ flickcurl_invoke_get_form_content(flickcurl *fc, int* count_p)
     count++; /* counting separators so need +1 for number of contents */
   }
   
-  /* Allocate count + 1 sized array of char* (key, value) pointers
+  /* Allocate 1+ count + 1 sized array of char* (key, value) pointers
    * The last pair are always (NULL, NULL).
    *
    * The pointers are into the 'content' buffer which is kept around
    * and owned by this array and stored in form[0].
    */
-  form = (char**)calloc(2*(count + 1), sizeof(char*));
+  form = (char**)calloc(1 + 2*(count + 1), sizeof(char*));
   if(!form) {
     if(content)
       free(content);
     return NULL;
   }
 
+  /* the form owns the content array */
+  form[0] = content;
+
   if(content) {
-    for(p = content, i = 0; *p; p++) {
+    for(p = content, i = 1; *p; p++) {
       char *start = p;
 
       while(*p && *p != '&' && *p != '=')
@@ -1590,8 +1603,6 @@ flickcurl_invoke_get_form_content(flickcurl *fc, int* count_p)
     }
     form[i++] = NULL;
     form[i] = NULL;
-
-    free(content);
   }
 
   if(count_p)
diff --git a/src/flickcurl_internal.h b/src/flickcurl_internal.h
index 4904341..3082978 100644
--- a/src/flickcurl_internal.h
+++ b/src/flickcurl_internal.h
@@ -119,7 +119,7 @@ xmlDocPtr flickcurl_invoke(flickcurl *fc);
 char* flickcurl_invoke_get_content(flickcurl *fc, size_t* size_p);
 /* Invoke URI prepared above and get back 'count' key/values */
 char** flickcurl_invoke_get_form_content(flickcurl *fc, int* count_p);
-void flickcurl_free_form(char **form, int count);
+void flickcurl_free_form(char **form);
 
 /* args.c */
 void flickcurl_free_arg(flickcurl_arg *arg);
diff --git a/src/oauth.c b/src/oauth.c
index d1f649e..8ac4e3c 100644
--- a/src/oauth.c
+++ b/src/oauth.c
@@ -741,11 +741,12 @@ flickcurl_oauth_create_request_token(flickcurl* fc, const char* callback)
           uri, count);
 #endif
 
-  for(i = 0; i < (2 * count); i += 2) {
-    if(!strcmp(form[i], "oauth_token")) {
-      request_token = form[i+1];
-    } else if(!strcmp(form[i], "oauth_token_secret")) {
-      request_token_secret = form[i+1];
+  for(i = 0; i < count; i++) {
+    int offset = 1 + (2 * i);
+    if(!strcmp(form[offset], "oauth_token")) {
+      request_token = form[offset+1];
+    } else if(!strcmp(form[offset], "oauth_token_secret")) {
+      request_token_secret = form[offset+1];
     }
   }
 
@@ -771,7 +772,7 @@ flickcurl_oauth_create_request_token(flickcurl* fc, const char* callback)
   
   tidy:
   if(form)
-    flickcurl_free_form(form, count);
+    flickcurl_free_form(form);
   
   return rc;
 }
@@ -888,15 +889,16 @@ flickcurl_oauth_create_access_token(flickcurl* fc, const char* verifier)
           uri, count);
 #endif
 
-  for(i = 0; i < (2 * count); i += 2) {
-    if(!strcmp(form[i], "oauth_token")) {
-      access_token = form[i+1];
-    } else if(!strcmp(form[i], "oauth_token_secret")) {
-      access_token_secret = form[i+1];
-    } else if(!strcmp(form[i], "username")) {
-      username = form[i+1];
-    } else if(!strcmp(form[i], "user_nsid")) {
-      user_nsid = form[i+1];
+  for(i = 0; i < count; i++) {
+    int offset = 1 + (2 * i);
+    if(!strcmp(form[offset], "oauth_token")) {
+      access_token = form[offset+1];
+    } else if(!strcmp(form[offset], "oauth_token_secret")) {
+      access_token_secret = form[offset+1];
+    } else if(!strcmp(form[offset], "username")) {
+      username = form[offset+1];
+    } else if(!strcmp(form[offset], "user_nsid")) {
+      user_nsid = form[offset+1];
     }
     /* ignoring: fullname */
   }
@@ -952,7 +954,7 @@ flickcurl_oauth_create_access_token(flickcurl* fc, const char* verifier)
   
   tidy:
   if(form)
-    flickcurl_free_form(form, count);
+    flickcurl_free_form(form);
   
   return rc;
 }
